home *** CD-ROM | disk | FTP | other *** search
- /* xlobj - xlisp object functions */
- /* Copyright (c) 1985, by David Michael Betz
- All Rights Reserved
- Permission is granted for unrestricted non-commercial use */
-
- #include "xlisp.h"
-
- #ifdef MEGAMAX
- overlay "overflow"
- #endif
-
- /* external variables */
- extern NODE *xlstack,*xlenv;
- extern NODE *s_stdout;
- extern NODE *self,*msgclass,*msgcls,*class,*object;
- extern NODE *new,*isnew;
-
- /* instance variable numbers for the class 'Class' */
- #define MESSAGES 0 /* list of messages */
- #define IVARS 1 /* list of instance variable names */
- #define CVARS 2 /* list of class variable names */
- #define CVALS 3 /* list of class variable values */
- #define SUPERCLASS 4 /* pointer to the superclass */
- #define IVARCNT 5 /* number of class instance variables */
- #define IVARTOTAL 6 /* total number of instance variables */
-
- /* number of instance variables for the class 'Class' */
- #define CLASSSIZE 7
-
- /* forward declarations */
- FORWARD NODE *entermsg();
- FORWARD NODE *findmsg();
- FORWARD NODE *sendmsg();
- FORWARD NODE *findvar();
- FORWARD NODE *getivar();
- FORWARD NODE *getcvar();
- FORWARD NODE *makelist();
-
- /* xlgetivar - get the value of an instance variable */
- NODE *xlgetivar(obj,num)
- NODE *obj; int num;
- {
- return (car(getivar(obj,num)));
- }
-
- /* xlsetivar - set the value of an instance variable */
- xlsetivar(obj,num,val)
- NODE *obj; int num; NODE *val;
- {
- rplaca(getivar(obj,num),val);
- }
-
- /* xlclass - define a class */
- NODE *xlclass(name,vcnt)
- char *name; int vcnt;
- {
- NODE *sym,*cls;
-
- /* create the class */
- sym = xlsenter(name);
- setvalue(sym,cls = newnode(OBJ));
- cls->n_obclass = class;
- cls->n_obdata = makelist(CLASSSIZE);
-
- /* set the instance variable counts */
- xlsetivar(cls,IVARCNT,cvfixnum((FIXNUM)vcnt));
- xlsetivar(cls,IVARTOTAL,cvfixnum((FIXNUM)vcnt));
-
- /* set the superclass to 'Object' */
- xlsetivar(cls,SUPERCLASS,object);
-
- /* return the new class */
- return (cls);
- }
-
- /* xladdivar - enter an instance variable */
- xladdivar(cls,var)
- NODE *cls; char *var;
- {
- NODE *ivar,*lptr;
-
- /* find the 'ivars' instance variable */
- ivar = getivar(cls,IVARS);
-
- /* add the instance variable */
- lptr = newnode(LIST);
- rplacd(lptr,car(ivar));
- rplaca(ivar,lptr);
- rplaca(lptr,xlsenter(var));
- }
-
- /* xladdmsg - add a message to a class */
- xladdmsg(cls,msg,code)
- NODE *cls; char *msg; NODE *(*code)();
- {
- NODE *mptr;
-
- /* enter the message selector */
- mptr = entermsg(cls,xlsenter(msg));
-
- /* store the method for this message */
- rplacd(mptr,newnode(SUBR));
- cdr(mptr)->n_subr = code;
- }
-
- /* xlsend - send a message to an object (message in arg list) */
- NODE *xlsend(obj,args)
- NODE *obj,*args;
- {
- NODE *oldstk,arglist,*msg,*val;
-
- /* find the message binding for this message */
- if ((msg = findmsg(obj->n_obclass,xlevmatch(SYM,&args))) == NIL)
- xlfail("no method for this message");
-
- /* evaluate the arguments and send the message */
- oldstk = xlsave(&arglist,NULL);
- arglist.n_ptr = xlevlist(args);
- val = sendmsg(obj,msg,arglist.n_ptr);
- xlstack = oldstk;
-
- /* return the result */
- return (val);
- }
-
- /* xlobgetvalue - get the value of an instance variable */
- int xlobgetvalue(sym,pval)
- NODE *sym,**pval;
- {
- NODE *bnd;
- if ((bnd = findvar(sym)) == NIL)
- return (FALSE);
- *pval = car(bnd);
- return (TRUE);
- }
-
- /* xlobsetvalue - set the value of an instance variable */
- int xlobsetvalue(sym,val)
- NODE *sym,*val;
- {
- NODE *bnd;
- if ((bnd = findvar(sym)) == NIL)
- return (FALSE);
- rplaca(bnd,val);
- return (TRUE);
- }
-
- /* obisnew - default 'isnew' method */
- LOCAL NODE *obisnew(args)
- NODE *args;
- {
- xllastarg(args);
- return (xlygetvalue(self));
- }
-
- /* obclass - get the class of an object */
- LOCAL NODE *obclass(args)
- NODE *args;
- {
- /* make sure there aren't any arguments */
- xllastarg(args);
-
- /* return the object's class */
- return (xlygetvalue(self)->n_obclass);
- }
-
- /* obshow - show the instance variables of an object */
- LOCAL NODE *obshow(args)
- NODE *args;
- {
- NODE *oldstk,fptr,*obj,*cls,*names;
- int ivtotal,n;
-
- /* create a new stack frame */
- oldstk = xlsave(&fptr,NULL);
-
- /* get the file pointer */
- fptr.n_ptr = (args ? xlgetfile(&args) : getvalue(s_stdout));
- xllastarg(args);
-
- /* get the object and its class */
- obj = xlygetvalue(self);
- cls = obj->n_obclass;
-
- /* print the object and class */
- xlputstr(fptr.n_ptr,"Object is ");
- xlprint(fptr.n_ptr,obj,TRUE);
- xlputstr(fptr.n_ptr,", Class is ");
- xlprint(fptr.n_ptr,cls,TRUE);
- xlterpri(fptr.n_ptr);
-
- /* print the object's instance variables */
- for (cls = obj->n_obclass; cls; cls = xlgetivar(cls,SUPERCLASS)) {
- names = xlgetivar(cls,IVARS);
- ivtotal = getivcnt(cls,IVARTOTAL);
- for (n = ivtotal - getivcnt(cls,IVARCNT); n < ivtotal; ++n) {
- xlputstr(fptr.n_ptr," ");
- xlprint(fptr.n_ptr,car(names),TRUE);
- xlputstr(fptr.n_ptr," = ");
- xlprint(fptr.n_ptr,xlgetivar(obj,n),TRUE);
- xlterpri(fptr.n_ptr);
- names = cdr(names);
- }
- }
-
- /* restore the previous stack frame */
- xlstack = oldstk;
-
- /* return the object */
- return (obj);
- }
-
- /* obsendsuper - send a message to an object's superclass */
- LOCAL NODE *obsendsuper(args)
- NODE *args;
- {
- NODE *obj,*super,*msg;
-
- /* get the object */
- obj = xlygetvalue(self);
-
- /* get the object's superclass */
- super = xlgetivar(obj->n_obclass,SUPERCLASS);
-
- /* find the message binding for this message */
- if ((msg = findmsg(super,xlmatch(SYM,&args))) == NIL)
- xlfail("no method for this message");
-
- /* send the message */
- return (sendmsg(obj,msg,args));
- }
-
- /* clnew - create a new object instance */
- LOCAL NODE *clnew()
- {
- NODE *oldstk,obj,*cls;
-
- /* create a new stack frame */
- oldstk = xlsave(&obj,NULL);
-
- /* get the class */
- cls = xlygetvalue(self);
-
- /* generate a new object */
- obj.n_ptr = newnode(OBJ);
- obj.n_ptr->n_obclass = cls;
- obj.n_ptr->n_obdata = makelist(getivcnt(cls,IVARTOTAL));
-
- /* restore the previous stack frame */
- xlstack = oldstk;
-
- /* return the new object */
- return (obj.n_ptr);
- }
-
- /* clisnew - initialize a new class */
- LOCAL NODE *clisnew(args)
- NODE *args;
- {
- NODE *ivars,*cvars,*super,*cls;
- int n;
-
- /* get the ivars, cvars and superclass */
- ivars = xlmatch(LIST,&args);
- cvars = (args ? xlmatch(LIST,&args) : NIL);
- super = (args ? xlmatch(OBJ,&args) : object);
- xllastarg(args);
-
- /* get the new class object */
- cls = xlygetvalue(self);
-
- /* store the instance and class variable lists and the superclass */
- xlsetivar(cls,IVARS,ivars);
- xlsetivar(cls,CVARS,cvars);
- xlsetivar(cls,CVALS,makelist(listlength(cvars)));
- xlsetivar(cls,SUPERCLASS,super);
-
- /* compute the instance variable count */
- n = listlength(ivars);
- xlsetivar(cls,IVARCNT,cvfixnum((FIXNUM)n));
- n += getivcnt(super,IVARTOTAL);
- xlsetivar(cls,IVARTOTAL,cvfixnum((FIXNUM)n));
-
- /* return the new class object */
- return (cls);
- }
-
- /* clanswer - define a method for answering a message */
- LOCAL NODE *clanswer(args)
- NODE *args;
- {
- NODE *oldstk,arg,msg,fargs,code;
- NODE *obj,*mptr,*fptr;
-
- /* create a new stack frame */
- oldstk = xlsave(&arg,&msg,&fargs,&code,NULL);
-
- /* initialize */
- arg.n_ptr = args;
-
- /* message symbol, formal argument list and code */
- msg.n_ptr = xlmatch(SYM,&arg.n_ptr);
- fargs.n_ptr = xlmatch(LIST,&arg.n_ptr);
- code.n_ptr = xlmatch(LIST,&arg.n_ptr);
- xllastarg(arg.n_ptr);
-
- /* get the object node */
- obj = xlygetvalue(self);
-
- /* make a new message list entry */
- mptr = entermsg(obj,msg.n_ptr);
-
- /* setup the message node */
- rplacd(mptr,fptr = newnode(LIST));
- rplaca(fptr,fargs.n_ptr);
- rplacd(fptr,code.n_ptr);
-
- /* restore the previous stack frame */
- xlstack = oldstk;
-
- /* return the object */
- return (obj);
- }
-
- /* entermsg - add a message to a class */
- LOCAL NODE *entermsg(cls,msg)
- NODE *cls,*msg;
- {
- NODE *ivar,*lptr,*mptr;
-
- /* find the 'messages' instance variable */
- ivar = getivar(cls,MESSAGES);
-
- /* lookup the message */
- for (lptr = car(ivar); lptr != NIL; lptr = cdr(lptr))
- if (car(mptr = car(lptr)) == msg)
- return (mptr);
-
- /* allocate a new message entry if one wasn't found */
- lptr = newnode(LIST);
- rplacd(lptr,car(ivar));
- rplaca(ivar,lptr);
- rplaca(lptr,mptr = newnode(LIST));
- rplaca(mptr,msg);
-
- /* return the symbol node */
- return (mptr);
- }
-
- /* findmsg - find the message binding given an object and a class */
- LOCAL NODE *findmsg(cls,sym)
- NODE *cls,*sym;
- {
- NODE *lptr,*msg;
-
- /* look for the message in the class or superclasses */
- for (msgcls = cls; msgcls != NIL; ) {
-
- /* lookup the message in this class */
- for (lptr = xlgetivar(msgcls,MESSAGES); lptr != NIL; lptr = cdr(lptr))
- if ((msg = car(lptr)) != NIL && car(msg) == sym)
- return (msg);
-
- /* look in class's superclass */
- msgcls = xlgetivar(msgcls,SUPERCLASS);
- }
-
- /* message not found */
- return (NIL);
- }
-
- /* sendmsg - send a message to an object */
- LOCAL NODE *sendmsg(obj,msg,args)
- NODE *obj,*msg,*args;
- {
- NODE *oldstk,oldenv,newenv,method,cptr,val,*isnewmsg;
-
- /* create a new stack frame */
- oldstk = xlsave(&oldenv,&newenv,&method,&cptr,&val,NULL);
-
- /* get the method for this message */
- method.n_ptr = cdr(msg);
-
- /* make sure its a function or a subr */
- if (!subrp(method.n_ptr) && !consp(method.n_ptr))
- xlfail("bad method");
-
- /* create a new environment frame */
- newenv.n_ptr = xlframe(NIL);
- oldenv.n_ptr = xlenv;
-
- /* bind the symbols 'self' and 'msgclass' */
- xlbind(self,obj,newenv.n_ptr);
- xlbind(msgclass,msgcls,newenv.n_ptr);
-
- /* evaluate the function call */
- if (subrp(method.n_ptr)) {
- xlenv = newenv.n_ptr;
- val.n_ptr = (*method.n_ptr->n_subr)(args);
- }
- else {
-
- /* bind the formal arguments */
- xlabind(car(method.n_ptr),args,newenv.n_ptr);
- xlenv = newenv.n_ptr;
-
- /* execute the code */
- cptr.n_ptr = cdr(method.n_ptr);
- while (cptr.n_ptr != NIL)
- val.n_ptr = xlevarg(&cptr.n_ptr);
- }
-
- /* restore the environment */
- xlenv = oldenv.n_ptr;
-
- /* after creating an object, send it the "isnew" message */
- if (car(msg) == new && val.n_ptr != NIL) {
- if ((isnewmsg = findmsg(val.n_ptr->n_obclass,isnew)) == NIL)
- xlfail("no method for the isnew message");
- sendmsg(val.n_ptr,isnewmsg,args);
- }
-
- /* restore the previous stack frame */
- xlstack = oldstk;
-
- /* return the result value */
- return (val.n_ptr);
- }
-
- /* getivcnt - get the number of instance variables for a class */
- LOCAL int getivcnt(cls,ivar)
- NODE *cls; int ivar;
- {
- NODE *cnt;
- if ((cnt = xlgetivar(cls,ivar)) == NIL || !fixp(cnt))
- xlfail("bad value for instance variable count");
- return ((int)cnt->n_int);
- }
-
- /* findvar - find a class or instance variable */
- LOCAL NODE *findvar(sym)
- NODE *sym;
- {
- NODE *obj,*cls,*names;
- int ivtotal,n;
-
- /* get the current object and the message class */
- obj = xlygetvalue(self);
- cls = xlygetvalue(msgclass);
- if (!(objectp(obj) && objectp(cls)))
- return (NIL);
-
- /* find the instance or class variable */
- for (; objectp(cls); cls = xlgetivar(cls,SUPERCLASS)) {
-
- /* check the instance variables */
- names = xlgetivar(cls,IVARS);
- ivtotal = getivcnt(cls,IVARTOTAL);
- for (n = ivtotal - getivcnt(cls,IVARCNT); n < ivtotal; ++n) {
- if (car(names) == sym)
- return (getivar(obj,n));
- names = cdr(names);
- }
-
- /* check the class variables */
- names = xlgetivar(cls,CVARS);
- for (n = 0; consp(names); ++n) {
- if (car(names) == sym)
- return (getcvar(cls,n));
- names = cdr(names);
- }
- }
-
- /* variable not found */
- return (NIL);
- }
-
- /* getivar - get an instance variable */
- LOCAL NODE *getivar(obj,num)
- NODE *obj; int num;
- {
- NODE *ivar;
-
- /* get the instance variable */
- for (ivar = obj->n_obdata; num > 0; num--)
- if (ivar != NIL)
- ivar = cdr(ivar);
- else
- xlfail("bad instance variable list");
-
- /* return the instance variable */
- return (ivar);
- }
-
- /* getcvar - get a class variable */
- LOCAL NODE *getcvar(cls,num)
- NODE *cls; int num;
- {
- NODE *cvar;
-
- /* get the class variable */
- for (cvar = xlgetivar(cls,CVALS); num > 0; num--)
- if (cvar != NIL)
- cvar = cdr(cvar);
- else
- xlfail("bad class variable list");
-
- /* return the class variable */
- return (cvar);
- }
-
- /* listlength - find the length of a list */
- LOCAL int listlength(list)
- NODE *list;
- {
- int len;
- for (len = 0; consp(list); len++)
- list = cdr(list);
- return (len);
- }
-
- /* makelist - make a list of nodes */
- LOCAL NODE *makelist(cnt)
- int cnt;
- {
- NODE *oldstk,list,*lnew;
-
- /* make the list */
- oldstk = xlsave(&list,NULL);
- for (; cnt > 0; cnt--) {
- lnew = newnode(LIST);
- rplacd(lnew,list.n_ptr);
- list.n_ptr = lnew;
- }
- xlstack = oldstk;
-
- /* return the list */
- return (list.n_ptr);
- }
-
- /* xloinit - object function initialization routine */
- xloinit()
- {
- /* don't confuse the garbage collector */
- class = object = NIL;
-
- /* enter the object related symbols */
- self = xlsenter("SELF");
- msgclass = xlsenter("MSGCLASS");
- new = xlsenter(":NEW");
- isnew = xlsenter(":ISNEW");
-
- /* create the 'Class' object */
- class = xlclass("CLASS",CLASSSIZE);
- class->n_obclass = class;
-
- /* create the 'Object' object */
- object = xlclass("OBJECT",0);
-
- /* finish initializing 'class' */
- xlsetivar(class,SUPERCLASS,object);
- xladdivar(class,"IVARTOTAL"); /* ivar number 6 */
- xladdivar(class,"IVARCNT"); /* ivar number 5 */
- xladdivar(class,"SUPERCLASS"); /* ivar number 4 */
- xladdivar(class,"CVALS"); /* ivar number 3 */
- xladdivar(class,"CVARS"); /* ivar number 2 */
- xladdivar(class,"IVARS"); /* ivar number 1 */
- xladdivar(class,"MESSAGES"); /* ivar number 0 */
- xladdmsg(class,":NEW",clnew);
- xladdmsg(class,":ISNEW",clisnew);
- xladdmsg(class,":ANSWER",clanswer);
-
- /* finish initializing 'object' */
- xladdmsg(object,":ISNEW",obisnew);
- xladdmsg(object,":CLASS",obclass);
- xladdmsg(object,":SHOW",obshow);
- xladdmsg(object,":SENDSUPER",obsendsuper);
- }